using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using PulsePrototype.PulseManagers;
using PulsePrototype.PulseManagers.Components;
using PulsePrototype.Messages;
using PulsePrototype.Messages.PositionMessages;


namespace PulsePrototype
{
    /// <summary>
    /// This is a game component that implements IUpdateable.
    /// </summary>
    public class BaseEntity
    {
        public Vector3 position = Vector3.Zero;
        public Matrix rotation = Matrix.Identity;
        public int uniqueID;

        private SceneManager sceneManager;
        private string name;
        private ComponentCollection components;

        public int UniqueID
        {
            get { return this.uniqueID; }
            set { this.uniqueID = value; }
        }

        public SceneManager Manager
        {
            get { return sceneManager; }
        }

        public String Name
        {
            get { return name; }
        }

        public ComponentCollection Components
        {
            get { return components; }
            set { components = value; }
        }

        public BaseEntity(SceneManager sceneManager, String EntityName, int uniqueid)
        {
            this.components = new ComponentCollection(this);
            this.sceneManager = sceneManager;
            this.name = EntityName;
            this.uniqueID = uniqueid;
        }

        public void AddComponent(BaseComponent component)
        {
            if (components.Contains(component))
            {
                throw new Exception("Component type " + component.Name + " already exists on this object: " + name);
            }

            components.Add(component);
        }

        public void RemoveComponent(BaseComponent component)
        {
            if (!components.Contains(component))
            {
                throw new Exception("Component type " + component.Name + " doesn't exists on this object: " + name);
            }

            components.Remove(component);
        }

        public BaseComponent GetComponent(String componentName)
        {
            BaseComponent component = null;
            if (components.Contains(componentName))
            {
                component = components[componentName];
            }

            return component;
        }

        /// <summary>
        /// Returns the first component with the specified type.
        /// This is simply a shortcut for (T)Components[typeof(T)].
        /// </summary>
        /// <typeparam name="T">The type of the component.</typeparam>
        public T GetComponent<T>() where T : BaseComponent
        {
            var Component = this.Components[typeof(T)];
            return (T)Component;
        }

        public bool SendMessage(BaseMessage msg)
        {
            bool messageHandled = false;

            // Entity has a switch for any messages it cares about
            switch (msg.messageType)
            {
                case MessageType.SetPosition:
                    {
                        MsgSetPosition msgSetPos = msg as MsgSetPosition;
                        position.X = msgSetPos.x;
                        position.Y = msgSetPos.y;
                        position.Z = msgSetPos.z;

                        messageHandled = true;
                        //Console.WriteLine("Entity handled SetPosition");
                    }
                    break;
                case MessageType.GetPosition:
                    {
                        MsgGetPosition msgGetPos = msg as MsgGetPosition;
                        msgGetPos.x = position.X;
                        msgGetPos.y = position.Y;
                        msgGetPos.z = position.Z;

                        messageHandled = true;
                        //Console.WriteLine("Entity handled GetPosition");
                    }
                    break;
                default:
                    return PassMessageToComponents(msg);
            }

            // If the entity didn't handle the message but the component
            // did, we return true to signify it was handled by something.
            messageHandled |= PassMessageToComponents(msg);

            return messageHandled;
        }

        /// <summary>
        /// If any component can and does handle the message, return true.
        /// </summary>
        /// <param name="msg"></param>
        /// <returns></returns>
        private bool PassMessageToComponents(BaseMessage msg)
        {
            bool messageHandled = false;

            // "x |= y" is equivalent to " x = x OR y"
            // Good old computer science logic.
            foreach (BaseComponent comp in components)
            {
                messageHandled |= comp.SendMessage(msg);
            }

            // Would have worked if we were using a List, but nope.
            // this.components.ForEach(c => messageHandled |= c.SendMessage(msg));

            return messageHandled;
        }


        /// <summary>
        /// Allows the game component to update itself.
        /// </summary>
        /// <param name="gameTime">Provides a snapshot of timing values.</param>
        public void Update(GameTime gameTime)
        {
            foreach (BaseComponent comp in components)
            {
                comp.Update(gameTime);
            }
        }

        public void Draw(GameTime gameTime, List<RenderDescription> renderDescriptions)
        {
            // Components must have a draw method, but it can be empty
            foreach (BaseComponent comp in components)
            {
                comp.Draw(gameTime, renderDescriptions);
            }
        }
    }
}
